package com.store.orm.mysql.generator;

import com.store.orm.mysql.MySQLUtil;
import com.store.orm.mysql.handler.ConvertTypeHandler;
import com.store.orm.util.FieldUtil;
import com.store.orm.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static com.store.orm.common.KeyWord.*;


/**
 * @author 宫静雨
 * @version 1.0
 */
public class TableToPojo {

    private Connection connection;
    private MySQLUtil UTIL;
    private static final Logger logger = LoggerFactory.getLogger(PojoToTable.class);
    private static final String USER_DIR = System.getProperty("user.dir").replaceAll("\\\\", "/");

    public TableToPojo(MySQLUtil UTIL) {
        this.UTIL = UTIL;
        this.connection = UTIL.getConnection();
    }

    public void execute(String packageName, String... tables) {
        for (String table : tables) {
            help(packageName, table);
        }
    }

    public void execute(String packageName) {
        List<String> tables = getTables();
        tables.forEach(table -> help(packageName, table));
    }

    private void help(String packageName, String table) {
        String path = USER_DIR + "/src/main/java/" + packageName.replaceAll("\\.", "/");
        Map<String, String> parse = parse(table);
        String setupImport = setupImport(packageName);
        String setupHead = setupHead(table);
        String setupConstructor = setupConstructor(table, parse);
        String setupField = setupField(parse);
        String setupSetter = setupSetter(parse);
        String setupGetter = setupGetter(parse);
        String result = setupImport + setupHead + setupConstructor + setupField +
                setupSetter + setupGetter + CRLF + RIGHT_BIG_BRACKET;
        try {
            File file = new File(path + "/" + FieldUtil.hump(table) + ".java");
            FileWriter fw = new FileWriter(file);
            fw.write(result);
            fw.flush();
            fw.close();
            if (UTIL.isLogged()) {
                logger.info("生成实体类的文件路径: {}", file.getPath());
            }
        } catch (Exception e) {
            logger.error("生成实体类时发生错误: {}", e.getMessage());
        }
    }

    /**
     * 创建 import 相关类
     *
     * @return import
     */
    private String setupImport(String packageName) {
        return "package " + packageName + SEMICOLON + CRLF + CRLF +
                "import java.io.Serializable;" + CRLF + "import java.util.Date;" + CRLF + CRLF;
    }

    /**
     * 创建头信息, 格式如下:
     * public class Test implement Serializable {
     *
     * @param table 表名
     * @return 头信息
     */
    private String setupHead(String table) {
        return PUBLIC + CLASS + FieldUtil.hump(table) + BLANK +
                IMPLEMENT + "Serializable" + BLANK + LEFT_BIG_BRACKET;
    }

    /**
     * 创建所有私有字段, 类型如下:
     * private Integer id;
     *
     * @param fieldMap 经过 parse 方法后的 Map
     * @return 解析后的私有字段
     */
    private String setupField(Map<String, String> fieldMap) {
        StringBuilder sb = new StringBuilder();
        fieldMap.forEach((k, v) -> sb.append(CRLF).append(TAB).append(PRIVATE).append(ConvertTypeHandler.TP.get(v).getSimpleName())
                .append(BLANK).append(k).append(SEMICOLON).append(CRLF));
        return sb.toString();
    }

    /**
     * 创建构造器, 包括全参和空参, 类型如下：
     * public Test() {
     * }
     * public Test(Integer id){
     * this.id = id;
     * }
     *
     * @param fieldMap 经过 parse 方法后的 Map
     * @return 解析后的构造器
     */
    private String setupConstructor(String table, Map<String, String> fieldMap) {
        StringBuilder sb = new StringBuilder();
        // 空参构造器
        sb.append(CRLF).append(TAB).append(PUBLIC).append(FieldUtil.hump(table)).append(LEFT_ROUND_BRACKET)
                .append(RIGHT_ROUND_BRACKET).append(BLANK).append(LEFT_BIG_BRACKET).append(CRLF)
                .append(TAB).append(RIGHT_BIG_BRACKET);
        sb.append(CRLF);
        // 全参构造器
        sb.append(CRLF).append(TAB).append(PUBLIC).append(FieldUtil.hump(table)).append(LEFT_ROUND_BRACKET);
        fieldMap.forEach((k, v) -> sb.append(ConvertTypeHandler.TP.get(v).getSimpleName()).append(BLANK)
                .append(k).append(COMMA).append(BLANK));
        sb.delete(sb.length() - 2, sb.length());
        sb.append(RIGHT_ROUND_BRACKET).append(BLANK).append(LEFT_BIG_BRACKET);
        fieldMap.forEach((k, v) -> sb.append(CRLF).append(TAB).append(TAB).append(THIS).append(DOT).append(k).append(EQUAL_TO)
                .append(k).append(SEMICOLON));
        sb.append(CRLF).append(TAB).append(RIGHT_BIG_BRACKET).append(CRLF);
        return sb.toString();
    }

    /**
     * 创建所有 Getter 方法, 类型如下:
     * public Integer getId() {
     * return id;
     * }
     *
     * @param fieldMap 经过 parse 方法后的 Map
     * @return 解析后的 getter 方法
     */
    private String setupGetter(Map<String, String> fieldMap) {
        StringBuilder sb = new StringBuilder();
        fieldMap.forEach((k, v) -> sb.append(CRLF).append(TAB).append(PUBLIC).append(ConvertTypeHandler.TP.get(v).getSimpleName())
                .append(BLANK).append("get").append(StringUtil.firstUpper(k)).append(LEFT_ROUND_BRACKET).append(RIGHT_ROUND_BRACKET)
                .append(BLANK).append(LEFT_BIG_BRACKET).append(CRLF).append(TAB).append(TAB).append(RETURN)
                .append(k).append(SEMICOLON).append(CRLF).append(TAB).append(RIGHT_BIG_BRACKET).append(CRLF));
        return sb.toString();
    }

    /**
     * 创建所有 Setter 方法, 类型如下:
     * public void setId(Integer id) {
     * this.id = id;
     * }
     *
     * @param fieldMap 经过 parse 方法后的 Map
     * @return 解析后的 setter 方法
     */
    private String setupSetter(Map<String, String> fieldMap) {
        StringBuilder sb = new StringBuilder();
        fieldMap.forEach((k, v) -> sb.append(CRLF).append(TAB).append(PUBLIC).append(VOID).append("set").append(StringUtil.firstUpper(k))
                .append(LEFT_ROUND_BRACKET).append(ConvertTypeHandler.TP.get(v).getSimpleName()).append(BLANK).append(k)
                .append(RIGHT_ROUND_BRACKET).append(BLANK).append(LEFT_BIG_BRACKET).append(CRLF).append(TAB).append(TAB)
                .append(THIS).append(DOT).append(k).append(EQUAL_TO).append(k).append(SEMICOLON).append(CRLF)
                .append(TAB).append(RIGHT_BIG_BRACKET).append(CRLF));
        return sb.toString();
    }

    /**
     * 获取数据库的名称，以便其他方法使用
     *
     * @return 数据库名称
     */
    private String getDatabase() {
        try {
            String url = connection.getMetaData().getURL();
            String database;
            if (url.contains("?")) {
                database = url.split("\\?")[0].split("/")[3];
            } else {
                database = url.split("/")[3];
            }
            return database;
        } catch (SQLException e) {
            logger.error("获取数据库失败: {}", e.getMessage());
            throw new RuntimeException();
        }
    }

    /**
     * 获取到提供的数据库所有表
     *
     * @return 表的集合
     */
    private List<String> getTables() {
        List<String> list = new ArrayList<>();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            String database = getDatabase();
            ResultSet tables = metaData.getTables(database, PER_CENT, PER_CENT, null);
            while (tables.next()) {
                String table_name = tables.getString("TABLE_NAME");
                list.add(table_name);
            }
            logger.info("数据库中的表: {}", list);
        } catch (SQLException e) {
            logger.error("获取数据库表时发生错误: {}", e.getMessage());
        }
        return list;
    }

    /**
     * 解析一张数据库表, 将它的字段和类型存放到一个 Map 中, 其中键为字段名, 值为字段类型
     *
     * @param table 数据库表名
     * @return 数据库表结构
     */
    private Map<String, String> parse(String table) {
        Map<String, String> map = new LinkedHashMap<>();
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet columns = metaData.getColumns(getDatabase(), PER_CENT, table, null);
            while (columns.next()) {
                String column_name = columns.getString("COLUMN_NAME");
                String type_name = columns.getString("TYPE_NAME");
                if (UTIL.isCamelCase()) {
                    column_name = FieldUtil.mysqlToPojo(column_name);
                }
                map.put(column_name, type_name);
            }
        } catch (SQLException e) {
            logger.error("解析表时发生错误: {}", e.getMessage());
        }
        return map;
    }
}